<?php if(! defined('SYSPATH')) exit('No direct script access allowed');
/*
 +----------------------------------------------------------------------+
 | QuickPHP Framework Version 0.10                                      |
 +----------------------------------------------------------------------+
 | Copyright (c) 2010 QuickPHP.net All rights reserved.                 |
 +----------------------------------------------------------------------+
 | Licensed under the Apache License, Version 2.0 (the 'License');      |
 | you may not use this file except in compliance with the License.     |
 | You may obtain a copy of the License at                              |
 | http://www.apache.org/licenses/LICENSE-2.0                           |
 | Unless required by applicable law or agreed to in writing, software  |
 | distributed under the License is distributed on an 'AS IS' BASIS,    |
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or      |
 | implied. See the License for the specific language governing         |
 | permissions and limitations under the License.                       |
 +----------------------------------------------------------------------+
 | Author: BoPo <ibopo@126.com>                                         |
 +----------------------------------------------------------------------+
*/
/**
 * Acl(访问控制列表)。
 *
 * <code>
 * $acl['member']['allow'] = array(
 * 'Sns'=>'*',
 * 'Blog'=>'*',
 * );
 *
 * $acl['anonymous']['allow'] = array(
 * 'Blog'=>'index',
 * );
 *
 * $acl['member']['deny'] = array(
 * 'Sns'=>array('banUser', 'showVipHome'),
 * 'Blog' =>array('deleteComment', 'writePost')
 * );
 *
 * $acl['admin']['allow'] = '*';
 * $acl['admin']['deny'] = array(
 * 'Sns'=>array('showVipHome')
 * );
 *
 * </code>
 *
 * <code>
 * QuickPHP::acl()->rules = $acl;
 * QuickPHP::acl()->defaultFailedRoute = '/error';
 * </code>
 *
 * @category    QuickPHP
 * @package     Acl
 * @author      BoPo <ibopo@126.com>
 * @copyright   Copyright &copy; 2010 QuickPHP
 * @license     http://www.quickphp.net/license/
 * @version     $Id: Cache.php 7215 2011-01-20 15:47:40Z bopo $
 */
class QuickPHP_Acl
{
    protected static $_instance;
    /**
     * 设置Acl规则。 定义在acl.php
     * @var array
     */
    public $rules;

    /**
     * 默认的控制规则,如果没有自定义失败控制规则是定义的某些规则。
     * @var string|array
     */
    public $defaultFailedRoute = array('/error-default/failed-route/please-set-in-route', 404);

    /**
     * 返回一个单身Acl的实例。
     *
     * @param   string  访问规则
     * @return  Cache
     */
    public static function instance(array $rules = Null)
    {
        if (!isset(Acl::$_instance))
            Acl::$_instance = new Acl($rules);

        return Acl::$_instance;
    }

    public function __construct($rules = NULL)
    {
        $this->rules = $rules;
    }

    /**
     * 检查用户角色是否可以访问资源、行为列表以及两者均可访问。
     *
     * <code>
     * QuickPHP::acl()->isAllowed('member', 'Blog', 'post' );
     * QuickPHP::acl()->isAllowed('member', 'blog');
     * </code>
     *
     * @param string 规则
     * @param string 资源
     * @param string 方法
     * @return bool
     */
    public function isAllowed($role, $resource, $action = '')
    {
        if($action == '')
        {
            return isset($this->rules[$role]['allow'][$resource]);
        }
        else
        {
            if(isset($this->rules[$role]['allow'][$resource]))
            {
                $actionlist = $this->rules[$role]['allow'][$resource];

                if($actionlist === '*')
                {
                    return TRUE;
                }
                else
                {
                    return in_array($action, $actionlist);
                }
            }
            else
            {
                return FALSE;
            }
        }
    }

    /**
     * 检查用户角色是否已经被屏蔽访问资源、行为列表以及两者均可访问。
     *
     * <code>
     * QuickPHP::acl()->isDenied('member', 'Blog', 'post' );
     * QuickPHP::acl()->isDenied('member', 'blog');
     * </code>
     *
     * @param string 规则
     * @param string 资源
     * @param string 方法
     * @return bool
     */
    public function isDenied($role, $resource, $action = '')
    {
        if($action == '')
        {
            return isset($this->rules[$role]['deny'][$resource]);
        }
        else
        {
            if(isset($this->rules[$role]['deny'][$resource]))
            {
                $actionlist = $this->rules[$role]['deny'][$resource];

                if($actionlist === '*')
                {
                    return TRUE;
                }
                else
                {
                    return in_array($action, $actionlist);
                }
            }
            else
            {
                return FALSE;
            }
        }
    }

    /**
     * 检查用户的角色,是能访问资源/行动。
     *
     * @param string 规则
     * @param string 资源
     * @param string 方法
     * @return array|string
     */
    public function process($role, $resource, $action = '')
    {
        if($this->isDenied($role, $resource, $action))
        {
            if(isset($this->rules[$role]['failRoute']))
            {
                $route = $this->rules[$role]['failRoute'];

                if(is_string($route))
                {
                    return array($route, 'internal');
                }
                else
                {
                    if(isset($route[$resource]))
                    {
                        return (is_string($route[$resource])) ? array($route[$resource], 'internal') : $route[$resource];
                    }
                    elseif(isset($route[$resource . '/' . $action]))
                    {
                        return (is_string($route)) ? array($route, 'internal') : $route[$resource . '/' . $action];
                    }
                    elseif(isset($route['_default']))
                    {
                        return (is_string($route['_default'])) ? array($route['_default'], 'internal') : $route['_default'];
                    }
                    else
                    {
                        return (is_string($this->defaultFailedRoute)) ? array($this->defaultFailedRoute, 404) : $this->defaultFailedRoute;
                    }
                }
            }
        }
        elseif($this->isAllowed($role, $resource, $action) == false)
        {
            if(isset($this->rules[$role]['failRoute']))
            {
                $route = $this->rules[$role]['failRoute'];

                if(is_string($route))
                {
                    return array($route, 'internal');
                }
                else
                {
                    if(isset($route[$resource]))
                    {
                        return (is_string($route[$resource])) ? array($route[$resource], 'internal') : $route[$resource];
                    }
                    elseif(isset($route[$resource . '/' . $action]))
                    {
                        return (is_string($route)) ? array($route, 'internal') : $route[$resource . '/' . $action];
                    }
                    elseif(isset($route['_default']))
                    {
                        return (is_string($route['_default'])) ? array($route['_default'], 'internal') : $route['_default'];
                    }
                    else
                    {
                        return (is_string($this->defaultFailedRoute)) ? array($this->defaultFailedRoute, 404) : $this->defaultFailedRoute;
                    }
                }
            }
        }
    }
}